#define _BSD_SOURCE 

#include<netdb.h>
#include"is_seqnum.h"

#define BACKLOG 50

int main(int argc , char ** argv)
{
    if(argc > 1 && !strcmp(argv[1],"--help"))
        usageErr("%s [init-seq-num]\n",argv[0]);
    char reqLenStr[INT_LEN];
    
    #define ADDRSTRLEN (NI_MAXHOST + NI_MAXSERV + 10)
    char addrStr[ADDRSTRLEN];
    char host[NI_MAXHOST];
    char service[NI_MAXSERV];
    // 服务器序号初始化为1 
    uint32_t seqNum = (argc > 1) ? getInt(argv[1],0,"init-seq-num") : 0;
    // 忽略SIGPIPE 信号
    if(signal(SIGPIPE,SIG_IGN) == SIG_ERR)
        errExit("signal");

    // 调用getaddrinfo 去 获取端口号PORT_NUM的TCP socket的socket地址结构图
    struct addrinfo hints , *result , *rp;
    memset(&hints,0,sizeof(struct addrinfo));

    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_UNSPEC ; //支持IPv4 or IPv6
    hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
    if(getaddrinfo(NULL , PORT_NUM , &hints , &result) != 0)
        errExit("getaddrinfo");

    int optval = 1,lfd;
    // 找到一个可用的地址结构
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        lfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (lfd == -1)
            continue;                   /* On error, try next address */

        if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1)
             errExit("setsockopt");

        if (bind(lfd, rp->ai_addr, rp->ai_addrlen) == 0)
            break;                      /* Success */

        /* bind() failed: close this socket and try next address */

        close(lfd);
    }

    if (rp == NULL)
        fatal("Could not bind socket to any address");

    if (listen(lfd, BACKLOG) == -1)
        errExit("listen");

    freeaddrinfo(result);
    char seqNumStr[INT_LEN];
    for (;;) {                  /* Handle clients iteratively */

        /* Accept a client connection, obtaining client's address */
        struct sockaddr_storage claddr;
        int addrlen = sizeof(struct sockaddr_storage);
        int cfd = accept(lfd, (struct sockaddr *) &claddr, &addrlen);
        if (cfd == -1) {
            errMsg("accept");
            continue;
        }

        if (getnameinfo((struct sockaddr *) &claddr, addrlen,
                    host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
            snprintf(addrStr, ADDRSTRLEN, "(%s, %s)", host, service);
        else
            snprintf(addrStr, ADDRSTRLEN, "(?UNKNOWN?)");
        printf("Connection from %s\n", addrStr);

        /* Read client request, send sequence number back */

        if (readLine(cfd, reqLenStr, INT_LEN) <= 0) {
            close(cfd);
            continue;                   /* Failed read; skip request */
        }

        int reqLen = atoi(reqLenStr);
        if (reqLen <= 0) {              /* Watch for misbehaving clients */
            close(cfd);
            continue;                   /* Bad request; skip it */
        }

        snprintf(seqNumStr, INT_LEN, "%d\n", seqNum);
        if (write(cfd, seqNumStr, strlen(seqNumStr)) != strlen(seqNumStr))
            fprintf(stderr, "Error on write");

        seqNum += reqLen;               /* Update sequence number */

        if (close(cfd) == -1)           /* Close connection */
            errMsg("close");
    }
}